//   Copyright 2015-2017 Ansersion
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

#include "utils.h"

#include <stdio.h>
#include <string.h>

#include "md5.h"

ssize_t
ReadLine(int fd, void* vptr, size_t maxlen) {
    size_t n;
    ssize_t rc;
    char c, *ptr;

    ptr = (char*)vptr;
    for (n = 1; n < maxlen; n++) {
    again:
        if ((rc = read(fd, &c, 1)) == 1) {
            *ptr++ = c;
            if (c == '\n')
                break;
        } else if (rc == 0) {
            *ptr = 0;
            return n - 1;
        } else {
            if (errno == EINTR)
                goto again;
            else if (errno == EWOULDBLOCK || errno == EAGAIN)
                return n - 1;
            return -1;
        }
    }
    *ptr = 0;
    return n;
}

ssize_t
Writen(int fd, const void* vptr, size_t n) {
    size_t nleft;
    ssize_t nwritten;
    const char* ptr;

    ptr = (const char*)vptr;
    nleft = n;
    while (nleft > 0) {
        if ((nwritten = write(fd, ptr, nleft)) <= 0) {
            if (nwritten < 0 && errno == EINTR)
                nwritten = 0; /* and call write() again */
            else if (nwritten < 0 && (errno == EWOULDBLOCK || errno == EAGAIN))
                return (n - nleft);
            else
                return (-1); /* error */
        }

        nleft -= nwritten;
        ptr += nwritten;
    }
    return (n);
}

ssize_t
Readn(int fd, void* vptr, size_t n) {
    size_t nleft;
    ssize_t nread;
    char* ptr;

    ptr = (char*)vptr;
    nleft = n;
    while (nleft > 0) {
        if ((nread = read(fd, ptr, nleft)) < 0) {
            if (errno == EINTR)
                nread = 0; /* and call read() again */
            else if (errno == EWOULDBLOCK || errno == EAGAIN)
                return (n - nleft);
            else
                return (-1);
        } else if (nread == 0)
            break; /* EOF */

        nleft -= nread;
        ptr += nread;
    }
    return (n - nleft); /* return >= 0 */
}
/* end readn */

int
Md5sum32(void* input, unsigned char* output, size_t input_size, size_t output_size) {
    unsigned char decrypt[16];
    // unsigned char decrypt_ascii[33];
    if (!input) {
        return -1;
    }
    if (!output) {
        return -2;
    }
    if (output_size < 32) {
        return -3;
    }
    // MD5_CTX md5;
    // MD5Init(&md5);
    // MD5Update(&md5, (unsigned char*)input, input_size);
    // MD5Final(&md5, decrypt);
    // for (int i = 0; i < 16; i++) {
    //     sprintf((char*)&decrypt_ascii[i * 2], "%02x", decrypt[i]);
    // }
    CMD5 m_md5;
    m_md5.GenerateMD5((unsigned char*)input, input_size);
    // decrypt_ascii[32] = '\0';
    memcpy(output, m_md5.ToString().c_str(), 32);
    return 0;
}

//字符串分割函数
std::vector<std::string>
split(std::string str, std::string pattern) {
    std::string::size_type pos;
    std::vector<std::string> result;
    str += pattern;  //扩展字符串以方便操作
    int size = str.size();
    for (int i = 0; i < size; i++) {
        pos = str.find(pattern, i);
        if (pos < size) {
            std::string s = str.substr(i, pos - i);
            result.push_back(s);
            i = pos + pattern.size() - 1;
        }
    }
    return result;
}

// base64编码
static const char base64Char[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

char*
base64Encode(char const* origSigned, unsigned origLength) {
    unsigned char const* orig = (unsigned char const*)origSigned;  // in case any input bytes have the MSB set
    if (orig == NULL)
        return NULL;

    unsigned const numOrig24BitValues = origLength / 3;
    bool havePadding = origLength > numOrig24BitValues * 3;
    bool havePadding2 = origLength == numOrig24BitValues * 3 + 2;
    unsigned const numResultBytes = 4 * (numOrig24BitValues + havePadding);
    char* result = new char[numResultBytes + 1];  // allow for trailing '\0'

    // Map each full group of 3 input bytes into 4 output base-64 characters:
    unsigned i;
    for (i = 0; i < numOrig24BitValues; ++i) {
        result[4 * i + 0] = base64Char[(orig[3 * i] >> 2) & 0x3F];
        result[4 * i + 1] = base64Char[(((orig[3 * i] & 0x3) << 4) | (orig[3 * i + 1] >> 4)) & 0x3F];
        result[4 * i + 2] = base64Char[((orig[3 * i + 1] << 2) | (orig[3 * i + 2] >> 6)) & 0x3F];
        result[4 * i + 3] = base64Char[orig[3 * i + 2] & 0x3F];
    }

    // Now, take padding into account.  (Note: i == numOrig24BitValues)
    if (havePadding) {
        result[4 * i + 0] = base64Char[(orig[3 * i] >> 2) & 0x3F];
        if (havePadding2) {
            result[4 * i + 1] = base64Char[(((orig[3 * i] & 0x3) << 4) | (orig[3 * i + 1] >> 4)) & 0x3F];
            result[4 * i + 2] = base64Char[(orig[3 * i + 1] << 2) & 0x3F];
        } else {
            result[4 * i + 1] = base64Char[((orig[3 * i] & 0x3) << 4) & 0x3F];
            result[4 * i + 2] = '=';
        }
        result[4 * i + 3] = '=';
    }

    result[numResultBytes] = '\0';
    return result;
}

// base64解码
std::string
b64decodestring(const std::string& strString) {
    int nByteSrc = strString.length();
    std::string pszSource = strString;

    const int dekey[] = {
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
        62,  // '+'
        -1, -1, -1,
        63,                                      // '/'
        52, 53, 54, 55, 56, 57, 58, 59, 60, 61,  // '0'-'9'
        -1, -1, -1, -1, -1, -1, -1, 0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14,
        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,  // 'A'-'Z'
        -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
        42, 43, 44, 45, 46, 47, 48, 49, 50, 51,  // 'a'-'z'
    };

    if (nByteSrc % 4 != 0)
        throw "bad base64 string";

    std::string pszDecode(nByteSrc * 3 / 4 + 4, '\0');
    int nLoop = pszSource[nByteSrc - 1] == '=' ? nByteSrc - 4 : nByteSrc;
    int b[4];
    int i = 0, n = 0;
    for (i = 0; i < nLoop; i += 4) {
        b[0] = dekey[pszSource[i]];
        b[1] = dekey[pszSource[i + 1]];
        b[2] = dekey[pszSource[i + 2]];
        b[3] = dekey[pszSource[i + 3]];
        if (b[0] == -1 || b[1] == -1 || b[2] == -1 || b[3] == -1)
            throw "bad base64 string";

        pszDecode[n] = (b[0] << 2) | ((b[1] & 0x30) >> 4);
        pszDecode[n + 1] = ((b[1] & 0xf) << 4) | ((b[2] & 0x3c) >> 2);
        pszDecode[n + 2] = ((b[2] & 0x3) << 6) | b[3];

        n += 3;
    }

    if (pszSource[nByteSrc - 1] == '=' && pszSource[nByteSrc - 2] == '=') {
        b[0] = dekey[pszSource[i]];
        b[1] = dekey[pszSource[i + 1]];
        if (b[0] == -1 || b[1] == -1)
            throw "bad base64 string";

        pszDecode[n] = (b[0] << 2) | ((b[1] & 0x30) >> 4);
        pszDecode[n + 1] = '\0';
    }

    if (pszSource[nByteSrc - 1] == '=' && pszSource[nByteSrc - 2] != '=') {
        b[0] = dekey[pszSource[i]];
        b[1] = dekey[pszSource[i + 1]];
        b[2] = dekey[pszSource[i + 2]];
        if (b[0] == -1 || b[1] == -1 || b[2] == -1)
            throw "bad base64 string";

        pszDecode[n] = (b[0] << 2) | ((b[1] & 0x30) >> 4);
        pszDecode[n + 1] = ((b[1] & 0xf) << 4) | ((b[2] & 0x3c) >> 2);
        pszDecode[n + 2] = '\0';
    }

    if (pszSource[nByteSrc - 1] != '=' && pszSource[nByteSrc - 2] != '=')
        pszDecode[n] = '\0';

    return pszDecode;
}
